boycot 搜索
avatar

boycot

Vue3上手, 使用vite实现一个简单的Todolist应用

Vite

Vite is an opinionated web dev build tool that serves your code via native ES Module imports during dev and bundles it with Rollup for production.

目前,Vue-cli 还未能直接搭建 vue3 应用,需要在 vue2 的项目上通过执行vue add next命令进行升级。

而 Vite 是官方提供的一个可以快速搭建 vue3 新工具,它是一个简易的 http 服务器,无需通过 webpack 打包即可实时解析 vue3 文件,并能实现热更新。

现在最新版的 vite 已经提供了一个简易模板,可以直接使用一下命令快速搭建

安装与启动

$ npx create-vite-app <project-name>
$ cd <project-name>
$ npm install
$ npm run dev

具体参考官方仓库: https://github.com/vuejs/vite#getting-started

Typescript

vite 最新版默认已经可以直接对 typescript 进行解析编译,直接在 vue 文件中的script标签下加入lang="ts"即可。

Sass

执行命令安装 sass, npm i sass -D, 然后在 vue 文件的style标签下加入lang="scss"即可,这些与以前 vue2 都是一样的。

Vuex

支持 vue3 的 vuex 最新版现在还未正式发版,需要使用npm i vuex@next -D来安装。
Vuex 的新语法进行了一些变更,你需要使用 Vuex.createStore 来创建,其他语法没有区别。

import Vuex from "vuex";
// or => import { createStore } from 'vuex'
export default Vuex.createStore({
  state: {},
  mutations: {},
  actions: {},
  modules: {},
});

然后在 main.js 引入这个文件并 use

import { createApp } from "vue";
import App from "./App.vue";
import store from "./store/index.js";
import "./assets/base.css";
createApp(App).use(store).mount("#app");

Vue Compostion Api

Vue3 最大的一个变化就是使用Vue Compostion Api, 现在官方已经提供了 中文文档

本次只用到了其中很少一部分的 API,而且可能使用方式不一定准确,仅供参考,望见谅。

功能设计

  • 对 TodoList 的操作:完成、取消、添加、删除
  • 可记录不同日期的 TodoList
  • 对记录了 TodoList 的日期进行标注

现在先来看一下最终项目 DEMO 成果

点击头部的日期会弹出日期选择,可选择其他日期。

实现

主要组件Card.vue组件,然后里面包含一个日期选择组件DatePicker.vue 为了练习一下 vuex 在 vue3 的使用,将当前的选择日期记录在了 vuex 中。

主要逻辑代码

// Card.vue
import { ref, reactive, computed, watch } from "vue";
import { useStore } from "vuex";
import { Todo, getTodoList, setTodoList } from "../model/todo";
import DatePicker from "./DatePicker.vue";
const weekArr: string[] = [
  "Sunday",
  "Monday",
  "Tuesday",
  "Wednesday",
  "Thursday",
  "Firday",
  "Saturday",
];
export default {
  name: "Card",
  components: {
    DatePicker,
  },
  directives: {
    focus(el) {
      el.focus();
    },
  },
  props: {
    date: String,
    showAddBtn: Boolean,
  },
  setup(props) {
    const store = useStore();
    const state = {
      editingValue: ref(""),
      showDatePicker: ref(false),
    };
    let todoList = reactive([]);
    watch(
      () => props.date,
      (val) => {
        todoList.length = 0;
        todoList.push(...getTodoList(props.date));
        // state.showDatePicker.value = false
      },
      {
        immediate: true,
      }
    );
    const weekDay = computed(() => weekArr[new Date(props.date).getDay()]);
    const formatterDate = computed((): string => {
      const arr: string[] = new Date(props.date).toDateString().split(" ");
      return `${arr[1]} ${arr[2]}, ${arr[3]}`;
    });
    const handleChecked = (item: Todo): void => {
      item.isChecked = !item.isChecked;
      setTodoList(props.date, todoList);
      store.commit("refreshTodoListDateArr");
    };
    const handleRemove = (index: number): void => {
      todoList.splice(index, 1);
      setTodoList(props.date, todoList);
      store.commit("refreshTodoListDateArr");
    };
    const handleAdd = (): void => {
      state.editingValue.value = "";
      todoList.push({
        content: "",
        isChecked: false,
        isEditing: true,
      });
    };
    const handleEditSubmit = (item: Todo, index: number): void => {
      if (item.isEditing) {
        if (state.editingValue.value) {
          item.content = state.editingValue.value;
          item.isEditing = false;
        } else {
          todoList.splice(index, 1);
        }
        setTodoList(props.date, todoList);
        store.commit("refreshTodoListDateArr");
      }
    };
    return {
      weekDay,
      formatterDate,
      todoList,
      handleChecked,
      handleRemove,
      handleAdd,
      handleEditSubmit,
      ...state,
    };
  },
};

组件功能实现都比较简单,本次只为了熟悉语法,所以具体代码逻辑在这就不进行讲解了。

项目打包

使用npm run build或者npx vite build即可打包项目。

Vite 的打包基于 Rollup

需要注意的是,vite 也是想 vue-cli 那样提供了配置文件的,在根目录下添加vite.config.js即可,例如本地打包后项目是通过 https://kongfandong.cn/todo 来访问的,如果使用默认的打包配置,会出现资源 404 的问题。这时需要给打包配置资源路径/todo,加入以下配置:

// vite.config.js
module.exports = {
  base: "/todo",
};

Vite 还提供了很多其他的配置,具体请参考: https://github.com/vuejs/vite/blob/master/src/node/config.ts

一个插曲

一开始,我使用 vuex 是使用getCurrentInstance获取 vue 的实例想像 vue2 那样类似this.$store.state来使用 vuex 的,就像下面的代码

import { computed, getCurrentInstance } from "vue";
export default {
  setup() {
    const { ctx } = getCuurentInstance();
    const selectedDate = computed(() => ctx.$store.state.selectedDate);
    return {
      selectedDate,
    };
  },
};

这样 ts 会报错,提示 ctx 的类型没找到,通过查源码发现 vue 并没有把 ctx 的类型抛出。然后我就改成了

import { computed, getCurrentInstance } from "vue";
export default {
  setup() {
    const instance: any = getCuurentInstance();
    const ctx = instance.ctx;
    const selectedDate = computed(() => ctx.$store.state.selectedDate);
    return {
      selectedDate,
    };
  },
};

然后一切如愿进行,开发环境下什么问题也没有了。但是,到了打包之后运行,浏览器就报错了

index.js:9 Uncaught TypeError: Cannot read property 'state' of undefined
    at index.js:9
    at n (index.js:1)
    at Object.get value [as value] (index.js:1)
    at Object.get (index.js:1)
    at Object.get (index.js:1)
    at index.js:9
    at Vn (index.js:1)
    at Proxy.<anonymous> (index.js:9)
    at Proxy.<anonymous> (index.js:1)
    at nt (index.js:1)
(anonymous) @ index.js:9
...

报错 state 没找到,程序上的 vuex 的$store 是 undefined。然后我查了好久也没找到解决办法,于是只能在 github 上提个 issue。

最后得到了解答,原来在setup()中是不能使用getCurrentInstance的!

具体 Issue: https://github.com/vuejs/vite/issues/156

最终的解决办法,引入 vuex 的 useStore 方法,就可以在 setup 中使用 vuex 了。

最后代码改为:

import { computed } from "vue";
import { useStore } from "vuex";
export default {
  setup() {
    const store = useStore();
    const selectedDate = computed(() => ctx.$store.state.selectedDate);
    return {
      selectedDate,
    };
  },
};

总结

这个小 Demo 的实现过来还是遇到了不少问题,但还是一一解决了,可以让我对 vue3 与 typescript 进行了初步的了解。目前只是因为还不太熟悉,但感觉 vue3 未来发展潜力还是很大的。

还有 Vite 这个工具好像也是很强大的,目测 Vue3 官方后面有可能会直接推荐使用该工具进行开发了,有可能会放弃的 Webpack 了。

乳白
杏仁黄
茉莉黄
麦秆黄
油菜花黄
佛手黄
篾黄
葵扇黄
柠檬黄
金瓜黄
藤黄
酪黄
香水玫瑰黄
淡密黄
大豆黄
素馨黄
向日葵黄
雅梨黄
黄连黄
金盏黄
蛋壳黄
肉色
鹅掌黄
鸡蛋黄
鼬黄
榴萼黄
淡橘橙
枇杷黄
橙皮黄
北瓜黄
杏黄
雄黄
万寿菊黄
菊蕾白
秋葵黄
硫华黄
柚黄
芒果黄
蒿黄
姜黄
香蕉黄
草黄
新禾绿
月灰
淡灰绿
草灰绿
苔绿
碧螺春绿
燕羽灰
蟹壳灰
潭水绿
橄榄绿
蚌肉白
豆汁黄
淡茧黄
乳鸭黄
荔肉白
象牙黄
炒米黄
鹦鹉冠黄
木瓜黄
浅烙黄
莲子白
谷黄
栀子黄
芥黄
银鼠灰
尘灰
枯绿
鲛青
粽叶绿
灰绿
鹤灰
淡松烟
暗海水绿
棕榈绿
米色
淡肉色
麦芽糖黄
琥珀黄
甘草黄
初熟杏黄
浅驼色
沙石黄
虎皮黄
土黄
百灵鸟灰
山鸡黄
龟背黄
苍黄
莱阳梨黄
蜴蜊绿
松鼠灰
橄榄灰
蟹壳绿
古铜绿
焦茶绿
粉白
落英淡粉
瓜瓤粉
蜜黄
金叶黄
金莺黄
鹿角棕
凋叶棕
玳瑁黄
软木黄
风帆黄
桂皮淡棕
猴毛灰
山鸡褐
驼色
茶褐
古铜褐
荷花白
玫瑰粉
橘橙
美人焦橙
润红
淡桃红
海螺橙
桃红
颊红
淡罂粟红
晨曦红
蟹壳红
金莲花橙
草莓红
龙睛鱼红
蜻蜓红
大红
柿红
榴花红
银朱
朱红
鲑鱼红
金黄
鹿皮褐
醉瓜肉
麂棕
淡银灰
淡赭
槟榔综
银灰
海鸥灰
淡咖啡
岩石棕
芒果棕
石板灰
珠母灰
丁香棕
咖啡
筍皮棕
燕颔红
玉粉红
金驼
铁棕
蛛网灰
淡可可棕
中红灰
淡土黄
淡豆沙
椰壳棕
淡铁灰
中灰驼
淡栗棕
可可棕
柞叶棕
野蔷薇红
菠萝红
藕荷
陶瓷红
晓灰
余烬红
火砖红
火泥棕
绀红
橡树棕
海报灰
玫瑰灰
火山棕
豆沙
淡米粉
初桃粉红
介壳淡粉红
淡藏花红
瓜瓤红
芙蓉红
莓酱红
法螺红
落霞红
淡玫瑰灰
蟹蝥红
火岩棕
赭石
暗驼棕
酱棕
栗棕
洋水仙红
谷鞘红
苹果红
铁水红
桂红
极光红
粉红
舌红
曲红
红汞红
淡绯
无花果红
榴子红
胭脂红
合欢红
春梅红
香叶红
珊瑚红
萝卜红
淡茜红
艳红
淡菽红
鱼鳃红
樱桃红
淡蕊香红
石竹红
草茉莉红
茶花红
枸枢红
秋海棠红
丽春红
夕阳红
鹤顶红
鹅血石红
覆盆子红
貂紫
暗玉紫
栗紫
葡萄酱紫
牡丹粉红
山茶红
海棠红
玉红
高粱红
满江红
枣红
葡萄紫
酱紫
淡曙红
唐菖蒲红
鹅冠红
莓红
枫叶红
苋菜红
烟红
暗紫苑红
殷红
猪肝紫
金鱼紫
草珠红
淡绛红
品红
凤仙花红
粉团花红
夹竹桃红
榲桲红
姜红
莲瓣红
水红
报春红
月季红
豇豆红
霞光红
松叶牡丹红
喜蛋红
鼠鼻红
尖晶玉红
山黎豆红
锦葵红
鼠背灰
甘蔗紫
石竹紫
苍蝇灰
卵石紫
李紫
茄皮紫
吊钟花红
兔眼红
紫荆红
菜头紫
鹞冠紫
葡萄酒红
磨石紫
檀紫
火鹅紫
墨紫
晶红
扁豆花红
白芨红
嫩菱红
菠根红
酢酱草红
洋葱紫
海象紫
绀紫
古铜紫
石蕊红
芍药耕红
藏花红
初荷红
马鞭草紫
丁香淡紫
丹紫红
玫瑰红
淡牵牛紫
凤信紫
萝兰紫
玫瑰紫
藤萝紫
槿紫
蕈紫
桔梗紫
魏紫
芝兰紫
菱锰红
龙须红
蓟粉红
电气石红
樱草紫
芦穗灰
隐红灰
苋菜紫
芦灰
暮云灰
斑鸠灰
淡藤萝紫
淡青紫
青蛤壳紫
豆蔻紫
扁豆紫
芥花紫
青莲
芓紫
葛巾紫
牵牛紫
紫灰
龙睛鱼紫
荸荠紫
古鼎灰
乌梅紫
深牵牛紫
银白
芡食白
远山紫
淡蓝紫
山梗紫
螺甸紫
玛瑙灰
野菊紫
满天星紫
锌灰
野葡萄紫
剑锋紫
龙葵紫
暗龙胆紫
晶石紫
暗蓝紫
景泰蓝
尼罗蓝
远天蓝
星蓝
羽扇豆蓝
花青
睛蓝
虹蓝
湖水蓝
秋波蓝
涧石蓝
潮蓝
群青
霁青
碧青
宝石蓝
天蓝
柏林蓝
海青
钴蓝
鸢尾蓝
牵牛花蓝
飞燕草蓝
品蓝
银鱼白
安安蓝
鱼尾灰
鲸鱼灰
海参灰
沙鱼灰
钢蓝
云水蓝
晴山蓝
靛青
大理石灰
海涛蓝
蝶翅蓝
海军蓝
水牛灰
牛角灰
燕颔蓝
云峰白
井天蓝
云山蓝
釉蓝
鸥蓝
搪磁蓝
月影白
星灰
淡蓝灰
鷃蓝
嫩灰
战舰灰
瓦罐灰
青灰
鸽蓝
钢青
暗蓝
月白
海天蓝
清水蓝
瀑布蓝
蔚蓝
孔雀蓝
甸子蓝
石绿
竹篁绿
粉绿
美蝶绿
毛绿
蔻梢绿
麦苗绿
蛙绿
铜绿
竹绿
蓝绿
穹灰
翠蓝
胆矾蓝
樫鸟蓝
闪蓝
冰山蓝
虾壳青
晚波蓝
蜻蜓蓝
玉鈫蓝
垩灰
夏云灰
苍蓝
黄昏灰
灰蓝
深灰蓝
玉簪绿
青矾绿
草原远绿
梧枝绿
浪花绿
海王绿
亚丁绿
镍灰
明灰
淡绿灰
飞泉绿
狼烟灰
绿灰
苍绿
深海绿
长石灰
苷蓝绿
莽丛绿
淡翠绿
明绿
田园绿
翠绿
淡绿
葱绿
孔雀绿
艾绿
蟾绿
宫殿绿
松霜绿
蛋白石绿
薄荷绿
瓦松绿
荷叶绿
田螺绿
白屈菜绿
河豚灰
蒽油绿
槲寄生绿
云杉绿
嫩菊绿
艾背绿
嘉陵水绿
玉髓绿
鲜绿
宝石绿
海沬绿
姚黄
橄榄石绿
水绿
芦苇绿
槐花黄绿
苹果绿
芽绿
蝶黄
橄榄黄绿
鹦鹉绿
油绿
象牙白
汉白玉
雪白
鱼肚白
珍珠灰
浅灰
铅灰
中灰
瓦灰
夜灰
雁灰
深灰